package ermao.array_list;

import java.util.Arrays;

/**
 * @Description 数组
 * @Author weisl
 * @Date 2024/1/18
 * @Version 1.0
 */
public class ArrayList<E> implements List<E> {

    /**
     * 默认初始化空间
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空元素
     */
    private static final Object[] DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA = {};

    /**
     * ArrayList 元素数组缓存区
     * <p>
     * transient 在实际应用中，我们可以根据需要，灵活使用transient关键字，以确保序列化对象的安全性和性能。尤其对于包含敏感数据的对象，使用transient可以避免敏感数据被序列化到磁盘中，从而保障数据的保密性。
     * 由此可见，transient关键字在Java编程中扮演着重要的角色，使用transient关键字可以有效地进行对象序列化和反序列化，优化程序性能，增加数据安全性。
     */
    transient Object[] elementData;

    /**
     * List 集合元素数量
     */
    private int size;

    /**
     * 初始化 ArrayList 阶段，如果不指定大小，默认会初始化一个空的元素。这个时候是没有默认长度的。
     * 那么什么时候给初始化的长度呢？是在首次添加元素的时候，因为所有的添加元素操作，也都是需要判断容量，以及是否扩容的。那么在 add 添加元素时统一完成这个事情，还是比较好处理的。
     * 之后就是随着元素的添加，容量是会不足的。当容量不足的是，需要进行扩容操作。同时还得需要把旧数据迁移到新的数组上。所以数据的迁移算是一个比较耗时的操作
     */
    public ArrayList() {
        // 默认给个空的元素，当开始添加元素的时候在初始化长度
        this.elementData = DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA;
    }


    /**
     * 这是一份简化后的 ArrayList#add 操作
     * <p>
     * 判断当前容量与初始化容量，使用 Math.max 函数取最大值最为最小初始化空间。
     * 接下来是判断 minCapacity 和元素的数量，是否达到了扩容。首次创建 ArrayList 是一定会扩容的，也就是初始化 DEFAULT_CAPACITY = 10 的容量。
     * Arrays.copyOf 实际上是创建一个新的空间数组，之后调用的 System.arraycopy 迁移到新创建的数组上。这样后续所有的扩容操作，也就都保持统一了。
     * ArrayList 扩容完成后，就是使用 elementData[size++] = e; 添加元素操作了。
     *
     * @param e
     * @return boolean
     */
    @Override
    public boolean add(E e) {
        // 确保内部容量
        int minCapacity = size + 1;
        // 空数组
        if (elementData == DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        // 判断是否需要扩容 最小容量 》 实际容量 需要扩容
        if (minCapacity > elementData.length) {
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0) {
                newCapacity = minCapacity;
            }

            elementData = Arrays.copyOf(elementData, newCapacity);
        }

        // 添加元素
        elementData[size++] = e;

        return true;
    }

    /**
     * 删除元素
     * ArrayList 的元素删除，就是在确定出元素位置后，使用 System.arraycopy 拷贝数据方式移动数据，把需要删除的元素位置覆盖掉。
     * 此外它还会把已经删除的元素设置为 null 一方面让我们不会在读取到这个元素，另外一方面也是为了 GC
     *
     * @param index
     * @return
     */
    @Override
    public E remove(int index) {
        // 0 1 2 3 4
        E oldValue = (E) elementData[index];
        // 计算 索引后面的 数据个数
        int numMoved = size - (index + 1);
        if (numMoved > 0) {
            // 从原始数组的某个位置，拷贝到目标对象的某个位置开始后n个元素
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
        return oldValue;
    }

    /**
     * 获取
     *
     * @param index
     * @return
     */
    @Override
    public E get(int index) {
        return (E) elementData[index];
    }
}
